package uk.ac.cam.bt288.Pico;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.AlgorithmParameters;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import biz.source_code.base64Coder.Base64Coder;

import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {
	/** Called when the activity is first created. */

	private final String Tag = "Pico";
	private String encKey = "1234567890123456";
	private byte[] iv = null;
	private DBAdapter db = null;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		//instance of database adapter
		db = new DBAdapter(this);

		String appPubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCikfx50Os0fyvImBLjSqLV5mA0VZl0s1RMZQsJA32/0G9bUhPXYppy0q/P+LgTAXWuA3rIAk/6AfZ1AfIyfoXG9zecSCv7Np4gZ40rQG+8jI9HznqJGw4gviP+Gs3R1VMk8PuZKHcD18ZAe+mQuWX+vemfytZudQyFrSjH5QYx0wIDAQAB";
		String picoPubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCg9u1dkUwA5UcJjXoaeS/4LTItgGPOAPu0z7vFQclBVIDVq5fnvB/Fv1IjKAMv4kTBcdswW/8PHUvYlUoVZ3v77b5dghALUIwOwlMptsJqdtKON/t5pZpExFJPOvS9mdy/2ejeaUD47Jy8DvoDWT9gayatUxgMb++etEyUHrXSSQIDAQAB";
		String picoPrivKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKD27V2RTADlRwmNehp5L/gtMi2AY84A+7TPu8VByUFUgNWrl+e8H8W/UiMoAy/iRMFx2zBb/w8dS9iVShVne/vtvl2CEAtQjA7CUym2wmp20o43+3mlmkTEUk869L2Z3L/Z6N5pQPjsnLwO+gNZP2BrJq1TGAxv7560TJQetdJJAgMBAAECgYB+SX1tvcNdqIUVb5RvFMmX8w29OVAUnhzdfWYam0SlJwF1oo0p2SUsZrCwuIHXnbTH+OY5iNQSLbAGGwrmxCw2+3SNbCiuEEUXECmUhPJbJue+y/xuKurgqsKF+/GjdhT6FyNbVT2BBy0LKi5jUzbLKIn8UFGgErCRhosnjxN0AQJBAM9M02xlJlwcaHsOl1fHcWa7ZwBReycfdMnQg8nCwPbn1cRKGahp0iWMd1k4iyO4cZZ8rqqAumKCbJhTLumVNAkCQQDGx3L/1wkJVNZHGvcrUOL4efvfkq6/Mu44ueRiNgCzXVlPb9Dtr3Bwx9M8rGvxfHc0EnSqIoQRYvA22zkp9bxBAkAjiZUZGQIJemlC5050ouFC7k+LE2zcWR0pV9Cv7u4rpbTU0KszXujCvSDcr7/SoNUAKmG7IxRQvPEm7+Oz8XsZAkAjy+Mfw3+zGH4GDAsa19Qn3v/GwUmK0QcpXACO1x7FLjhZH8/GHePny3R05rABquJklDZCNTkHUIPlvRDMR56BAkB4AghObMHQ2XdrjNTRN7LyXy1z/BzyNQ8teGNPMcqHr9HcqyUOC/UdPAxERFNeF5lpkCZV2YRvf8yb+4LW0Dj4";

		//insert into database
		/*
		db.load();
		long id = db.insertEntry(appPubKey, "test1", picoPubKey, picoPrivKey);

		Log.d(Tag, "Inserted row: "+id);
		db.close();
		 */

		//read every entry from database
		db.load();
		Cursor c = db.getAll();
		if (c.moveToFirst()) {
			do {
				DisplayEntry(c);
			} while (c.moveToNext());
		}
		
		Button btn = (Button) findViewById(R.id.btn_auth);

		btn.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				auth();
			}
		});
		
		//db.close();

	}
	
	public void auth () {
		Socket s = null;
		InputStream in;
		OutputStream out;
		Cursor c = db.getEntry(1);
		PrivateKey priKey = null;
		try {
			priKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64Coder.decode(c.getString(4).toCharArray())));
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		
		Log.d(Tag, new String(Base64Coder.encode(priKey.getEncoded())));

		byte[] servKey = new byte[1024];
		byte[] servMessage = new byte[1024];
		int servKeyLength = -1;
		int msgLength = -1;
		KeyPair temp = Keys.GenerateKeyPair();
		
		try {
			s = new Socket("10.0.2.2", 9999);
			in = s.getInputStream();
			out = s.getOutputStream();
			
			servKeyLength = in.read(servKey);
			
			Log.d(Tag, "Serv key length: "+servKeyLength);
			
			PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Arrays.copyOfRange(servKey, 0, servKeyLength)));
			Log.d(Tag, "servKey: "+new String(Base64Coder.encode(pubKey.getEncoded())));
			
			out.write(temp.getPublic().getEncoded());
			String randStr = Keys.nextSessionId();
			
			Log.d(Tag, "Rand str: "+randStr);
			byte[] sendData = Keys.encrypt(randStr, pubKey);
			Log.d(Tag, "SendData: "+new String(Base64Coder.encode(sendData)));
			out.write(sendData);
			out.flush();
			msgLength = in.read(servMessage);
			Log.d(Tag, "msgLength: "+msgLength);
			
			String rcvStr = Keys.decrypt(Arrays.copyOfRange(servMessage, 0, msgLength), temp.getPrivate());
			Log.d(Tag, "received string: "+rcvStr);
			if (!rcvStr.equals(randStr)) {
				Log.w(Tag, "Application authentication failed");
				return;
			}
			out.write(Base64Coder.decode(c.getString(3)));
			
			msgLength = in.read(servMessage);
			rcvStr = Keys.decrypt(Arrays.copyOfRange(servMessage, 0, msgLength), priKey);
			
			Log.d(Tag, "Strlen: "+msgLength+"   rcvStr: "+rcvStr);
			out.write(Keys.encrypt(rcvStr, pubKey));
			
		} catch (Exception e) {
			e.printStackTrace();
		}
				
	}

	public void DisplayEntry (Cursor c){
		Toast.makeText(this, "App key: " + c.getString(1)+"\n"+"Username: "+c.getString(2)+"\n", Toast.LENGTH_LONG).show();
		Log.d(Tag, "PubKey: "+c.getString(3));
		Log.d(Tag, "PrivKey: " + c.getString(4));
	}

	public String AESEncrypt (String data, String key) {
		try {
			SecretKeySpec k = new SecretKeySpec(key.getBytes(), "AES");

			Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
			c.init(Cipher.ENCRYPT_MODE, k);

			AlgorithmParameters params = c.getParameters();
			iv = params.getParameterSpec(IvParameterSpec.class).getIV();

			return new String(c.doFinal(data.getBytes()));
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public String AESDecrypt (String data, String key) {
		try {
			Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
			SecretKeySpec k =
				new SecretKeySpec(key.getBytes(), "AES");
			c.init(Cipher.DECRYPT_MODE, k, new IvParameterSpec(iv));
			return new String(c.doFinal(data.getBytes()));
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}